
#pragma once

#include <task.h>

#include "os_basic_common.hpp"


namespace os_basic {


    class TaskCriticalScope {
       public:
        TaskCriticalScope() {
            taskENTER_CRITICAL();
        }

        ~TaskCriticalScope() {
            taskEXIT_CRITICAL();
        }
    };


    class TaskBase {
       protected:
        TaskHandle_t _handle = nullptr;

        TaskBase() = default;

       public:
        void yield() {
            taskYIELD();
        }

        operator TaskHandle_t() {
            return _handle;
        }

        _MAKE__OBJECT__NONCOPYABLE(TaskBase);
    };


    template <typename Backend, StackDepthType StaticStackSize = 0>
    class Task : public TaskBase {
       protected:
#if configSUPPORT_STATIC_ALLOCATION > 0

        // 静态栈空间尺寸要么固定为0，表示使用动态栈空间；要么必须满足最小栈空间尺寸
        static_assert(StaticStackSize == 0 || StaticStackSize >= configMINIMAL_STACK_SIZE);

        // StaticStackSize 可以等于0，此时这个数组会变成0 长度数组，可能需要编译器支持
        // 每个任务需要两块内存空间，一块是TCB，一块是栈，所以任务实际占用的内存要比栈空间大。
        // 动态创建时，先分配固定的TCB 空间，再分配栈。

        // 如果静态栈空间值大于0，表示希望使用静态空间创建任务，则实际需要的静态空间尺寸是TCB 尺寸加上栈空间尺寸

        static constexpr size_t _STATIC_SPACE_SIZE = (StaticStackSize > 0) ? (StaticStackSize + sizeof(StaticTask_t)) : (0);

        StackType_t _static_space[_STATIC_SPACE_SIZE];
#endif


       private:
        /**
         * @brief 任务执行入口，执行任务对象内的run() 函数
         *
         * 创建任务时使用_task_entry_point 作为函数参数，同时传入Child 对象指针。
         *
         * @param param
         */
        static void _task_entry_point(void* param) {
            auto child_obj = reinterpret_cast<Backend*>(param);
            child_obj->run();
        }


       public:
        bool create(uint8_t priority, const char* name, StackDepthType dynamic_stack_depth = MIN_STACK_DEPTH) {
            if (priority > HIGHEST_PRIORITY) {
                priority = HIGHEST_PRIORITY;
            }

#if configSUPPORT_STATIC_ALLOCATION > 0

            if (StaticStackSize >= configMINIMAL_STACK_SIZE) {
                _handle = xTaskCreateStatic(
                    _task_entry_point,
                    name,
                    StaticStackSize,
                    reinterpret_cast<void*>(this),
                    priority,
                    _static_space + sizeof(StaticTask_t),
                    reinterpret_cast<StaticTask_t*>(_static_space));

                return true;
            }

#endif

            if (dynamic_stack_depth < MIN_STACK_DEPTH) {
                dynamic_stack_depth = MIN_STACK_DEPTH;
            }

            BaseType_t state = xTaskCreate(
                _task_entry_point,
                name,
                dynamic_stack_depth,
                reinterpret_cast<void*>(this),
                priority,
                &_handle);

            return state == pdPASS;
        }
    };

}  // namespace os_basic